"""电影应用服务模块

提供了处理电影相关业务逻辑的函数和服务类。
"""

import csv
import datetime
import json
from collections import defaultdict
from typing import IO, Any, Callable, Dict, Tuple

from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.db import transaction
from django.shortcuts import get_object_or_404

from movies.models import UserMoviePreferences
from movies.serializers import PreferencesSerializer

from .models import Movie


def user_preferences(user_id: int) -> Any:
    """
    获取用户电影偏好设置

    Args:
        user_id (int): 用户ID

    Returns:
        Any: 用户偏好设置数据
    """
    user_preferences = get_object_or_404(UserMoviePreferences, user_id=user_id)
    serializer = PreferencesSerializer(user_preferences.preferences)
    return serializer.data


def user_watch_history(user_id: int) -> dict[Any, Any]:
    """
    获取用户观看历史

    Args:
        user_id (int): 用户ID

    Returns:
        dict: 用户观看历史数据
    """
    user_preferences = get_object_or_404(UserMoviePreferences, user_id=user_id)
    return {"watch_history": user_preferences.watch_history}


def add_preference(user_id: int, new_preferences: Dict[str, Any]) -> None:
    """
    添加新偏好或更新现有偏好到用户的电影偏好设置中，
    使用defaultdict自动处理列表，避免重复条目。

    Args:
        user_id (int): 用户ID
        new_preferences (Dict[str, Any]): 包含要添加或更新的新偏好的字典
    """
    with transaction.atomic():
        user = get_object_or_404(get_user_model(), id=user_id)
        (
            user_preferences,
            created,
        ) = UserMoviePreferences.objects.select_for_update().get_or_create(
            user_id=user.id, defaults={"preferences": {}}
        )

        # 使用defaultdict自动处理列表创建
        # 将现有偏好转换为defaultdict以便于更新
        current_preferences = defaultdict(list, user_preferences.preferences)

        for key, value in new_preferences.items():
            # 确保值不在列表中以避免重复
            if value not in current_preferences[key]:
                current_preferences[key].append(value)

        # 将defaultdict转换回dict以确保与Django模型兼容
        user_preferences.preferences = dict(current_preferences)
        user_preferences.save()


def add_watch_history(user_id: int, movie_id: int) -> None:
    """
    添加新电影到用户的观看历史。

    Args:
        user_id (int): 用户ID
        movie_id (int): 电影ID
    """
    movie = get_object_or_404(Movie, id=movie_id)
    movie_info = {
        "title": movie.title,
        "year": movie.release_year,
        "director": movie.extra_data.get("directors", []),
        "genre": movie.genres,
    }
    user_preferences, created = UserMoviePreferences.objects.get_or_create(
        user_id=user_id, defaults={"watch_history": [movie_info]}
    )
    if not created:
        # 将新电影信息添加到现有的观看历史中
        current_watch_history = user_preferences.watch_history
        current_watch_history.append(movie_info)
        user_preferences.watch_history = current_watch_history
        user_preferences.save()


def create_or_update_movie(
    title: str,
    genres: list[str],
    country: str | None = None,
    extra_data: dict[Any, Any] | None = None,
    release_year: int | None = None,
) -> Tuple[Movie, bool]:
    """
    创建或更新电影实例的服务函数。

    Args:
        title (str): 电影标题
        genres (list[str]): 电影类型列表
        country (str | None): 国家
        extra_data (dict[Any, Any] | None): 额外数据
        release_year (int | None): 发布年份

    Returns:
        Tuple[Movie, bool]: 返回电影实例和是否为新创建的标志

    Raises:
        ValidationError: 当发布年份超出有效范围时抛出异常
    """
    # 确保发布年份在可接受范围内
    current_year = datetime.datetime.now().year
    if release_year is not None and (
        release_year < 1888 or release_year > current_year
    ):
        raise ValidationError(
            "The release year must be between 1888 and the current year."
        )

    # 尝试更新现有电影或创建新电影
    movie, created = Movie.objects.update_or_create(
        title=title,
        defaults={
            "genres": genres,
            "country": country,
            "extra_data": extra_data,
            "release_year": release_year,
        },
    )
    return movie, created


def parse_csv(file: IO[Any]) -> int:
    """
    解析CSV文件并处理其中的电影数据

    Args:
        file (IO[Any]): CSV文件对象

    Returns:
        int: 处理的电影数量
    """
    movies_processed = 0
    reader = csv.DictReader(file)
    for row in reader:
        create_or_update_movie(**row)
        movies_processed += 1
        return movies_processed


def parse_json(file: IO[Any]) -> int:
    """
    解析JSON文件并处理其中的电影数据

    Args:
        file (IO[Any]): JSON文件对象

    Returns:
        int: 处理的电影数量
    """
    movies_processed = 0
    data = json.load(file)
    for item in data:
        create_or_update_movie(**item)
        movies_processed += 1
        return movies_processed


class FileProcessor:
    """文件处理器类"""

    def process(self, file_path: str, file_type: str) -> int:
        """
        根据文件类型处理文件

        Args:
            file_path (str): 文件路径
            file_type (str): 文件类型

        Returns:
            int: 处理的电影数量

        Raises:
            ValidationError: 当文件类型无效时抛出异常
        """
        if file_type == "text/csv":
            movies_processed = self.process_file(file_path, parse_csv)
        elif file_type == "application/json":
            movies_processed = self.process_file(file_path, parse_json)
        else:
            raise ValidationError("Invalid file type")

        return movies_processed

    def process_file(
        self, file_path: str, parser_function: Callable[[str], int]
    ) -> int:
        """
        处理文件

        Args:
            file_path (str): 文件路径
            parser_function (Callable[[str], int]): 解析函数

        Returns:
            int: 处理的电影数量
        """
        return parser_function(file_path)
